package com.pi.shisan.component.impl;

import com.pi.shisan.common.NumberUtil;
import com.pi.shisan.component.AnimationComponent;
import com.pi.shisan.component.util.AnimationThread;
import com.pi.shisan.component.util.ComponentThread;
import com.pi.shisan.component.util.FastIntList;
import com.pi.shisan.component.util.SharedMethod;
import com.pi.shisan.component.util.SharedSource;
import com.pi.shisan.util.DefaultFont8x8;
import com.pi.shisan.util.spipoint.DataWriter;
import com.pi.shisan.util.spipoint.MultipleXyToSpiConverter;

import java.time.LocalTime;
import java.util.function.Supplier;

public class WeatherComponent extends AnimationComponent {

    private Supplier<WeatherInfo> weatherInfoSupplier;

    private WeatherRefreshThread weatherRefreshThread;

    /**
     * 上一次绘制过的所有点保存
     */
    private final FastIntList lastAllPoints = new FastIntList();

    private static final int[] C = DefaultFont8x8.getPointsAsXy(-5176852414552080384L);

    private static final int[] HYPHEN = DefaultFont8x8.getPointsAsXy(246290604621824L);

    private static final int[] TO = DefaultFont8x8.getPointsAsXy(1155450415978905600L);

    private int fromX, fromY;

    private Integer min, max;

    public WeatherComponent(MultipleXyToSpiConverter spi, DataWriter dataWriter) {
        super(spi, dataWriter);
    }

    private void setMinMax(Integer min, Integer max) {
        this.min = min;
        this.max = max;
        refresh();
    }

    public void setWeatherInfoSupplier(Supplier<WeatherInfo> weatherInfoSupplier) {
        this.weatherInfoSupplier = weatherInfoSupplier;
    }

    @Override
    protected AnimationThread prepareAnimation() {
        this.weatherRefreshThread = new WeatherRefreshThread(this);
        animationThread = new ScrollTextThread(this);
        this.weatherRefreshThread.start();
        return animationThread;
    }

    @Override
    public void refresh() {
        ((ScrollTextThread) animationThread).wakeup();
    }

    @Override
    protected void prepareDestroy() {
        if (weatherRefreshThread != null) {
            weatherRefreshThread.setDestroy();
        }
    }

    private boolean prepareLastPoints() {
        lastAllPoints.forEach(spi::delete);
        lastAllPoints.clear();
        Integer min = this.min, max = this.max;
        if (min == null || max == null) {
            return false;
        }
        return true;
    }

    /**
     * @param fromX
     * @return 返回节点终止坐标，比如长度是5，返回4（0 - 4）
     */
    private int doRefresh(int fromX) {
        int maxX = Integer.MIN_VALUE;
        final int startX = this.startX;
        int x = this.startX + fromX;
        int maxXY;

        int currNum = min;
        for (int j = 0; j < 2; j++) {
            if (currNum < 0) {
                maxX = Integer.MIN_VALUE;
                currNum = -currNum;
                maxXY = SharedMethod.drawPoints(
                        HYPHEN,
                        startX, this.startY, x, 0, maxX, 0,
                        this.width, this.height, this.spi, this.lastAllPoints
                );
                maxX = NumberUtil.toX(maxXY);
                x += maxX + 2;
            }
            int num = NumberUtil.getNumsOfNum(currNum);
            int n;
            int p;
            for (int i = num - 1; i >= 0; i--) {
                maxX = Integer.MIN_VALUE;
                if (i > 0) {
                    p = (int) (Math.pow(10, i));
                    n = currNum / p;
                    currNum -= p * n;
                } else {
                    n = currNum % 10;
                }
                maxXY = SharedMethod.drawPoints(
                        SharedSource.getFromNumCache(n),
                        startX, this.startY, x, fromY, maxX, 0,
                        this.width, this.height, this.spi, this.lastAllPoints
                );
                maxX = NumberUtil.toX(maxXY);
                x += maxX + 2;
            }
            currNum = max;
            if (j == 0) {
                maxX = Integer.MIN_VALUE;
                maxXY = SharedMethod.drawPoints(
                        TO,
                        startX, this.startY, x, fromY, maxX, 0,
                        this.width, this.height, this.spi, this.lastAllPoints
                );
                maxX = NumberUtil.toX(maxXY);
                x += maxX + 2;
            }
        }

        maxXY = SharedMethod.drawPoints(
                C,
                startX, this.startY, x, fromY, maxX, 0,
                this.width, this.height, this.spi, this.lastAllPoints
        );
        maxX = NumberUtil.toX(maxXY);
        x += maxX;
        return x;
    }

    public static class WeatherRefreshThread extends Thread {
        private WeatherComponent weatherComponent;
        private volatile boolean destroy = false;
        private volatile boolean finished = false;

        public WeatherRefreshThread(WeatherComponent weatherComponent) {
            this.weatherComponent = weatherComponent;
            setName("weather-refresher");
        }

        @Override
        public void run() {
            while (!destroy) {
                Supplier<WeatherInfo> weatherInfoSupplier = weatherComponent.weatherInfoSupplier;
                if (weatherInfoSupplier != null) {
                    WeatherInfo weatherInfo = weatherInfoSupplier.get();
                    if (weatherInfo != null) {
                        weatherComponent.setMinMax(weatherInfo.getMin(), weatherInfo.getMax());
                    } else {
                        weatherComponent.setMinMax(null, null);
                    }
                }
                LocalTime now = LocalTime.now();
                int sleep = 60 - now.getSecond();
                try {
                    Thread.sleep(sleep * 1000);
                } catch (InterruptedException e) {
                }
            }
            finished = true;
        }

        public void setDestroy() {
            this.destroy = true;
            interrupt();
            while (!finished) {
                interrupt();
                Thread.yield();
            }
        }

    }

    public static class ScrollTextThread extends AnimationThread {

        private WeatherComponent weatherComponent;

        private int space = 5;

        /**
         * state会一直++
         * 当state != state时 不能够await
         */
        private int state = 0;

        public ScrollTextThread(WeatherComponent weatherComponent) {
            this.weatherComponent = weatherComponent;
            setName("weather-text-scroll");
        }

        public void wakeup() {
            synchronized (this) {
                state++;
                this.notify();
            }
        }

        @Override
        public void setDestroy() {
            destroy = true;
            wakeup();
            interrupt();
        }

        @Override
        protected void doRun() {
            there: while (!destroy) {
                synchronized (this) {
                    while (!weatherComponent.prepareLastPoints()) {
                        weatherComponent.fromX = 0;
                        try {
                            this.wait();
                        } catch (InterruptedException e) {
                        }
                        continue there;
                    }
                }
                try {
                    int before = this.state;
                    Boolean isStop = ComponentThread.submit(() -> {
                        int fromX = weatherComponent.fromX;
                        int width = weatherComponent.width;
                        int overX = weatherComponent.doRefresh(fromX);
                        if (fromX == 0 && overX <= width) {
                            weatherComponent.flushData();
                            return true;
                        }
                        fromX -= 1;
                        if (overX < width - 1) {
                            if (overX >= 0) {
                                weatherComponent.doRefresh(overX + space + 1);
                                if (overX == 0) {
                                    fromX = space;
                                }
                            }
                        }
                        weatherComponent.fromX = fromX;
                        weatherComponent.flushData();
                        return false;
                    });
                    if (isStop != null && isStop) {
                        synchronized (this) {
                            if (before == state) {
                                this.wait();
                            }
                        }
                    }
                } catch (Exception e) {
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                }
            }
        }
    }

    public static class WeatherInfo {
        private Integer min;
        private Integer max;

        public Integer getMin() {
            return min;
        }

        public void setMin(Integer min) {
            this.min = min;
        }

        public Integer getMax() {
            return max;
        }

        public void setMax(Integer max) {
            this.max = max;
        }
    }
}
